/* 
 * Copyright 2011 Carnegie Mellon University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package edu.cmu.lti.dimple;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import edu.cmu.lti.dimple.mavuno.JudgedParaphrases;
import edu.cmu.lti.dimple.mavuno.LearnedParaphrases;
import edu.cmu.lti.dimple.mavuno.PerTermParaphraser;
import edu.cmu.lti.dimple.nlp.AbstractNLPTool;

/**
 * Implements DIMPLE algorithm (Shima & Mitamura, 2011). 
 * 
 * @author Hideki Shima
 *
 */
public class DimpleAlgorithm {

  private AbstractNLPTool nlpTool;
  
  public DimpleAlgorithm( AbstractNLPTool nlpTool ) {
    this.nlpTool = nlpTool;
  }
  
  public List<Double> calculateDimple( 
          List<LearnedParaphrases> lps, JudgedParaphrases jp, 
          String targetTerm, int listSize, boolean decayingWeight ) {
    List<Double> result = new ArrayList<Double>();
    
    // Proposed algorithm
    for ( LearnedParaphrases lp : lps ) {
      PerTermParaphraser ptp = lp.get(targetTerm);
      if (ptp==null){
        System.out.println("No paraphrase available: "+lp.getAlgorighm()+" for "+targetTerm);
        continue;
      }
      
      List<String> paraphrases = ptp.getParaphrases();
      
      List<Integer> diversityVector = calculateD( targetTerm, paraphrases );
      
      //DCG = Discounted Cumulative Gain. We didn't discount in TextInfer experiment
      double DCG = 0D;
      //iDCG = ideal DCG a.k.a. normalization factor, "Z" denominator.
      double iDCG = 0D;

      if (Parameters.VERBOSE) System.out.println("---- "+targetTerm+"\t"+lp.getAlgorighm()+" ----");
      int min = Math.min(listSize, paraphrases.size());
      for (int i=0; i<min;i++) {
        boolean inList = i<paraphrases.size(); 
        double Q = inList ? jp.getMean(targetTerm, paraphrases.get(i)) : 0;
        double D = inList ? diversityVector.get(i) : 0;
        double gain = Q * D;
        //No discount (decaying weight) used for the TextInfer paper experiments.
        double decayFactor = decayingWeight ? (Math.log(1+(i+1))/Math.log(2)) : 1D;
        DCG += gain(gain)/decayFactor;
        iDCG += gain(Parameters.MAX_DIVERSITY)/decayFactor;
        if (Parameters.VERBOSE) System.out.println(targetTerm +"\t"+ (inList?paraphrases.get(i):"N/A")+"\t"+D+"\t"+Q+"\t"+gain);
      }
      double nDCG = DCG/iDCG;
      if (Parameters.VERBOSE) System.out.println( "DIMPLE = "+nDCG );
      if (Parameters.VERBOSE) System.out.println();
      result.add(nDCG);
    }
    return result;
  }
  
  private List<Integer> calculateD( String targetTerm, List<String> paraphrases ) {

    List<List<String>> w1 = new ArrayList<List<String>>(paraphrases.size());
    List<List<String>> w2 = new ArrayList<List<String>>(paraphrases.size());
    for (int i=0; i<paraphrases.size(); i++) {
      List<String> cw = nlpTool.extractContentWords(paraphrases.get(i));
      Collections.sort(cw);
      w1.add(cw);
      List<String> cwStemmed = nlpTool.stemWords(cw);
      w2.add(cwStemmed);
    }

    Set<String> history1 = new HashSet<String>();
    Set<String> history2 = new HashSet<String>();
    List<Integer> diversityVector = new ArrayList<Integer>();
    //SPECIAL OPERATION for pairwise paraphrase
    {
      List<String> cw = nlpTool.extractContentWords(targetTerm);
      history1.addAll(cw);
      List<String> cwStemmed = nlpTool.stemWords(cw);
      history2.addAll(cwStemmed);
    }
    for (int i=0; i<paraphrases.size(); i++) {
      int diversity = 0;
      boolean empty = w1.get(i).size()==0 || w1.get(i).get(0).length()==0;
      if ( empty || PairFilter.intersect(history1, w1.get(i)) ) {
        diversity = 1;
      } else {
        if ( PairFilter.intersect(history2, w2.get(i)) ) {
          diversity = 2;
        } else {
          diversity = 3;
        }
      }
      history1.addAll(w1.get(i));
      history2.addAll(w2.get(i));
      diversityVector.add(diversity);
    }
    return diversityVector;
  }
  
  private double gain( double g ) {
//    return g;
    return Math.pow(2, g) - 1D;
  }

}
